﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.VersionControl.Client;
using System.IO;
using System.Xml.Linq;
using System.Reflection;

namespace UpdateProject
{
    class Program
    {
        private static string LogFile = @"\UpdateProject.txt";

        //private static bool ChangeFramework;

        private static string ServerAddress;

        private static string WorkspacePath;

        private static string RootPath;

        private static string SpecificVersionAssemblies = "PSE.Framework.";

        private static List<string> BaseAssemblyNames = new List<string>() 
        {
            { "PSE." },
            { "Sinacor." },
            { "IMercado." }
        };

        private static List<string> GacAssemblies = new List<string>() 
        {
            // PSE
            @"PSE.Framework.", 
            @"PSE.Communication.", 
            @"PSE.ProcessManager.", 
            @"PSE.Deployment.", 

            // iMercado
            @"IMercado.Framework.", 
            @"IMercado.ProcessManager.", 

            // Sinacor
            @"Sinacor.Infra.", 
            @"Sinacor.Communication."
        };


        private static List<string> FrameworkAssembliesFolders = new List<string>() 
        {
            // PSE
            @"\Compilados\PSE\Framework", 
            @"\Compilados\PSEComponents\Communication", 
            @"\Compilados\PSEComponents\ProcessManager\Client", 
            @"\Compilados\PSEComponents\ProcessManager\Common", 
            @"\Compilados\PSEComponents\ProcessManager\Data", 
            @"\Compilados\PSEComponents\ProcessManager\Management", 
            @"\Compilados\PSEComponents\ProcessManager\Server", 
            @"\Compilados\PSEComponents\ProcessManager\ServiceManager", 

            // iMercado
            @"\Compilados\iMercado\Framework", 
            @"\Compilados\iMercado\ProcessManager", 

            // Sinacor
            @"\Compilados\Sinacor\Core", 
            @"\Compilados\Sinacor\Common\Infra", 
            @"\Compilados\Sinacor\Servicos\Infra", 
            @"\Compilados\Sinacor\UI\Infra", 
            @"\Compilados\Sinacor\ExternalRef"
        };

        private static Dictionary<string, string> ExternalRefSourceFolders = new Dictionary<string, string>() 
        {
            // PSE
            { "PSE", @"\Source\Framework\ExternalRef" }, 
            // iMercado
            { "iMercado", @"\Source\Framework\ExternalRef" }, 
            // Sinacor
            { "SinaINFR", @"\Source\Infra_2.0\ExternalRef" } 
        };

        private static Dictionary<string, string> BaseAssemblyPaths = new Dictionary<string, string>() 
        {
            { "PSE", @"\Compilados\PSE" },
            { "Sina", @"\Compilados\Sinacor" },
            { "iMercado", @"\Compilados\iMercado" }
        };

        private static List<string> FileExtensions = new List<string>() { "*.csproj" };


        //private static List<string> Branchs = new List<string>() 
        //{ 
        //    @"DEV",
        //    @"MAIN",
        //    @"ACE",
        //    @"PROD"
        //};

        private static List<string> BranchPaths = new List<string>();

        //private static Dictionary<string, List<string>> FrameworkTeamProjects = new Dictionary<string, List<string>>() 
        //{ 
        //    //{ "PSE", new List<string>() { @"\NET\DEV\CDSC\VN", @"\NET\MAIN\VC\VC", @"\NET\ACE\P1I3\VC", @"\NET\ACE\P1I2\VC", @"\NET\ACE\P1I3\VC", @"\NET\PROD\P1I1\HTX", @"\NET\PROD\P1I1\SPK" , @"\NET\PROD\P1I1\RTM", @"\NET\PROD\P1I3\HTX", @"\NET\PROD\P1I3\SPK" , @"\NET\PROD\P1I3\RTM" } },
        //    { "SinaINFR", new List<string>() { @"\NET\DEV\CDSC\VN", @"\NET\MAIN\VC\VC", @"\NET\ACE\P1I7\VC", @"\NET\PROD\P1I7\SPK", @"\NET\PROD\P1I7\HTX" } }, 
        //    //{ "iMercado", new List<string>() { @"\DEV\CDSC", @"\MAIN", @"\ACE\P1I2", @"\PROD\P1I2\HTX", @"\PROD\P1I2\SPK" } }
        //};

        //private static Dictionary<string, List<string>> BusinessTeamProjects = new Dictionary<string, List<string>>() 
        //{ 
        //    //{ "iMercado", new List<string>() { @"\NET\DEV\CDSB\VN", @"\NET\MAIN\VC\VC", @"\NET\ACE\P1I2\VC", @"\NET\PROD\P1I2\HTX", @"\NET\PROD\P1I2\SPK" } },
        //    //{ "SinaCOMU", new List<string>() { @"\NET\DEV\CDSB\VN", @"\NET\MAIN\VC\VC", @"\NET\ACE\P1I7\VC", @"\NET\PROD\P1I7\HTX", @"\NET\PROD\P1I7\SPK" } },
        //    //{ "SinaCCO", new List<string>() { @"\NET\DEV\CDSB\VN", @"\NET\MAIN\VC\VC", @"\NET\ACE\P1I7\VC", @"\NET\PROD\P1I7\HTX", @"\NET\PROD\P1I7\SPK" } },
        //    //{ "SinaCOMP", new List<string>() { @"\NET\DEV\CDSB\VN", @"\NET\MAIN\VC\VC", @"\NET\ACE\P1I7\VC", @"\NET\PROD\P1I7\HTX", @"\NET\PROD\P1I7\SPK" } },
        //    //{ "SinaIAPO", new List<string>() { @"\DEV\CDSB", @"\NET\MAIN\VC\VC", @"\NET\ACE\P1I7\VC", @"\NET\PROD\P1I7\HTX", @"\NET\PROD\P1I7\SPK" } },
        //    { "SinaOMF", new List<string>() { @"\NET\DEV\CDSB\VN", @"\NET\MAIN\VC\VC", @"\NET\ACE\P1I7\VC", @"\NET\PROD\P1I7\HTX", @"\NET\PROD\P1I7\SPK" } },
        //    //{ "SinaORD", new List<string>() { @"\NET\DEV\CDSB\VN", @"\NET\MAIN\VC\VC", @"\NET\ACE\P1I7\VC", @"\NET\PROD\P1I7\HTX", @"\NET\PROD\P1I7\SPK" } },
        //    //{ "SinaSOL", new List<string>() { @"\NET\DEV\CDSB\VN", @"\NET\MAIN\VC\VC", @"\NET\ACE\P1I3\VC", @"\NET\PROD\P1I3\HTX", @"\NET\PROD\P1I3\SPK" } },
        //    //{ "SinaTES", new List<string>() { @"\NET\DEV\CDSB\VN", @"\NET\MAIN\VC\VC", @"\NET\ACE\P1I7\VC", @"\NET\PROD\P1I7\HTX", @"\NET\PROD\P1I7\SPK" } },
        //    //{ "SinaCLI", new List<string>() { @"\NET\DEV\CDSB\VN", @"\NET\MAIN\VC\VC", @"\NET\ACE\P1I7\VC", @"\NET\PROD\P1I7\HTX", @"\NET\PROD\P1I7\SPK" } }
        //};


        static void Main(string[] args)
        {
            try
            {
                //ChangeFramework = Convert.ToBoolean(args[0]);
                ServerAddress = args[0];
                WorkspacePath = args[1];
                RootPath = args[2];

                for (int i = 3; i < args.Length; i++)
                {
                    BranchPaths.Add(args[i]);
                }
            }
            catch
            {
                Console.WriteLine(@"UpdateProject.exe ServerAddress WorkspacePath RootPath Path1 Path2 PathN");
                Console.WriteLine(@"Example:");
                Console.WriteLine(@"UpdateProject.exe utilcorp05 \DSEPROJ \DSEPROJ \DSEPROJ\SinaOMF\NET\DEV\CDSB\VN \DSEPROJ\SinaOMF\NET\MAIN\VC\VC");
                //Console.ReadKey();
                return;
            }

            ChangeProjects();
        }

        private static void ChangeProjects()
        {
            int count = 0, countTotal = 0;
            //List<string> externalRefAssemblies = null;
            List<FrameworkAssembly> frameworkAssemblies = null;

            //string root = Path.GetFullPath(WorkspacePath);

            TeamFoundationServer tfs = TeamFoundationServerFactory.GetServer(ServerAddress);
            VersionControlServer versionControlServer = (VersionControlServer)tfs.GetService(typeof(VersionControlServer));
            Workspace workspace = versionControlServer.GetWorkspace(Path.GetFullPath(WorkspacePath));


            if (File.Exists(Path.GetFullPath(LogFile)))
                File.Delete(Path.GetFullPath(LogFile));


            // Captura lista de dlls da infra
            frameworkAssemblies = GetFrameworkAssemblies();


            foreach (string branchFolder in BranchPaths)
            {
                string path = Path.GetFullPath(branchFolder);

                if (Directory.Exists(path))
                {
                    Console.WriteLine("Alterando pasta: " + path);

                    // Capturando branch atual
                    //string branch = Branchs.First(b => branchFolder.Contains(b));

                    //// Capturando lista de dlls ExternalRef
                    //if (ChangeFramework)
                    //    externalRefAssemblies = GetExternalRefAssemblies(teamProject, path);


                    // Capturando arquivos dos projetos
                    List<string> files = new List<string>();
                    foreach (string filter in FileExtensions)
                        files = Directory.GetFiles(path, filter, SearchOption.AllDirectories).Concat(files).ToList();


                    foreach (string file in files)
                    {
                        bool fileUpdated = false;

                        XDocument project = XDocument.Load(file);

                        ModifyReferenceSGBDUtil(project, ref fileUpdated);

                        ChangePlatformTarget(project, ref fileUpdated);

                        ChangePlatformTargetReference(project, ref fileUpdated);

                        RemoveObsoletsTags(project, ref fileUpdated);

                        if (ChangeProjectsFrameworkReferences(project, file, frameworkAssemblies))
                            fileUpdated = true;

                        //if (ChangeFramework)
                        //{
                        //    if (ChangeFrameworkProjectsReferences(project, file, externalRefAssemblies))
                        //        fileUpdated = true;
                        //}
                        //else
                        //{
                        if (ChangeBusinessProjectsReferences(project, file, frameworkAssemblies))
                            fileUpdated = true;
                        //}

                        if (ChangeOutputPath(project, file))
                            fileUpdated = true;

                        if (RemoveServiceReferences(project, file))
                            fileUpdated = true;

                        if (fileUpdated)
                        {
                            Console.WriteLine(file);
                            count++;

                            // checkout e salvar
                            workspace.PendEdit(file);
                            project.Save(file);
                        }
                    }

                    countTotal += count;

                    Console.WriteLine("Arquivos alterados: " + count);
                    Console.WriteLine();

                    count = 0;
                }
            }

            Console.WriteLine("Fim do processamento, arquivos alterados: " + countTotal);
            //Console.ReadKey();
        }

        private static void ModifyReferenceSGBDUtil(XDocument document, ref bool fileUpdated)
        {
            List<XElement> elements = (from c in document.Descendants()
                                       where (c.Name.LocalName.Equals("Reference") &&
                                       (c.Attributes().ToList().Find(e => e.Name.LocalName == "Include" && e.Value.StartsWith("Sinacor.Servicos.Comum.Util.SGBD")) != null))
                                       select (XElement)c).ToList();

            if (elements.Count() > 0)
            {
                foreach (XElement element in elements)
                {
                    XElement hintPaht = element.Elements().ToList().Find(e => e.Name.LocalName == "HintPath");

                    if (hintPaht != null && hintPaht.Value.Contains("ExternalRef"))
                    {
                        hintPaht.Value = hintPaht.Value.Replace("ExternalRef", @"Servicos\Comum\Bin");
                        fileUpdated = true;
                    }
                }
            }
        }

        private static void ChangePlatformTarget(XDocument document, ref bool fileUpdated)
        {
            var qProp = from c in document.Descendants()
                        where c.Name.LocalName.Equals("PropertyGroup") &&
                              c.Elements().ToList().Find(e => e.Name.LocalName == "OutputPath") != null
                        select (XElement)c;

            foreach (XElement group in qProp)
            {
                XElement platform = group.Elements().ToList().Find(e => e.Name.LocalName == "PlatformTarget");

                if (platform != null && platform.Value != "x86")
                {
                    platform.Value = "x86";
                    fileUpdated = true;
                }
                else
                {
                    if (group.Elements().ToList().Find(e => e.Name.LocalName == "ProjectGuid") == null &&
                        group.Elements().ToList().Find(e => e.Name.LocalName == "PlatformTarget") == null)
                    {
                        XNamespace ns = group.Name.Namespace;
                        platform = new XElement(ns + "PlatformTarget", "x86");
                        group.Add(platform);
                        fileUpdated = true;
                    }

                    var elements = group.Elements().ToList().Where(e => e.Name.LocalName == "PlatformTarget").ToList();

                    if (elements.Count > 1)
                    {
                        foreach (var element in elements)
                        {
                            element.Remove();
                        }

                        XNamespace ns = group.Name.Namespace;
                        platform = new XElement(ns + "PlatformTarget", "x86");
                        group.Add(platform);
                        fileUpdated = true;
                    }
                }
            }
        }

        private static void ChangePlatformTargetReference(XDocument document, ref bool fileUpdated)
        {
            int stringPos = 0;
            string newAttValue = string.Empty;

            var qRef = from c in document.Descendants()
                       where c.Name.LocalName.Equals("Reference")
                       select (XElement)c;

            foreach (XElement reference in qRef)
            {
                XAttribute refAttribute = reference.Attributes().ToList().Find(e => e.Name.LocalName == "Include" && (e.Value.ToUpper().Contains("SINACOR") || e.Value.ToUpper().Contains("PSE") || e.Value.ToUpper().Contains("IMERCADO")));

                if (refAttribute != null)
                {
                    stringPos = refAttribute.Value.IndexOf("processorArchitecture");

                    if (refAttribute.Value.Contains("processorArchitecture"))
                        newAttValue = string.Concat(refAttribute.Value.Substring(0, stringPos), "processorArchitecture=", "x86");
                    else
                        newAttValue = string.Concat(refAttribute.Value, ", processorArchitecture=", "x86");

                    if (refAttribute.Value != newAttValue)
                    {
                        refAttribute.Value = newAttValue;
                        fileUpdated = true;
                    }
                }
            }
        }

        private static void RemoveObsoletsTags(XDocument document, ref bool fileUpdated)
        {
            List<XElement> elements = (from c in document.Descendants()
                                       where c.Name.LocalName.Equals("add") && c.Attributes().ToList().Find(a => a.Name.LocalName == "key" &&
                                           (a.Value == "Sinacor.Infra.Service.Security.Services.OperationLoggingEnabled" ||
                                           a.Value == "Providers" ||
                                           a.Value == "EndpointConfigPath" ||
                                           a.Value == "ClientSettingsProvider.ServiceUri" ||
                                           a.Value == "Issuer")) != null
                                       select (XElement)c).ToList();

            if (elements.Count() > 0)
            {
                foreach (XElement element in elements)
                {
                    element.Remove();
                }

                fileUpdated = true;
            }
        }

        private static bool ChangeFrameworkProjectsReferences(XDocument project, string file, List<string> externalRefAssemblies)
        {
            bool fileUpdated = false;

            // Captura todas as tags Reference/ProjectReference em que o atributo Include aponte 
            // para uma dll/projeto de infra e que tenham algum dos elementos SpecificVersion, 
            // HintPath ou Private incorretos
            var frameworkRefs = from r in project.Descendants()
                                where
                                    // Framework Reference
                                    (
                                        r.Name.LocalName == "Reference" &&
                                        BaseAssemblyNames.Any(g => r.Attribute("Include").Value.StartsWith(g)) &&
                                        (
                                    // SpecificVersion deve ser true para PSE e false para todas as outras
                                            r.Element(XName.Get("SpecificVersion", r.Name.NamespaceName)) == null ||
                                            Convert.ToBoolean(r.Element(XName.Get("SpecificVersion", r.Name.NamespaceName)).Value) != r.Attribute("Include").Value.StartsWith(SpecificVersionAssemblies) ||
                                    // HintPath deve estar correto
                                            r.Element(XName.Get("HintPath", r.Name.NamespaceName)) == null ||
                                            r.Element(XName.Get("HintPath", r.Name.NamespaceName)).Value != GetRelativePath(file, r.Element(XName.Get("HintPath", r.Name.NamespaceName)).Value) ||
                                            (
                                    // Copylocal deve sempre ser false para dlls do GAC
                                                GacAssemblies.Any(g => r.Attribute("Include").Value.StartsWith(g)) &&
                                                (
                                                    r.Element(XName.Get("Private", r.Name.NamespaceName)) == null ||
                                                    Convert.ToBoolean(r.Element(XName.Get("Private", r.Name.NamespaceName)).Value) == true
                                                )
                                            ) ||
                                            (
                                    // Copylocal não deve existir para outras dlls
                                                !GacAssemblies.Any(g => r.Attribute("Include").Value.StartsWith(g)) &&
                                                r.Element(XName.Get("Private", r.Name.NamespaceName)) != null
                                            )
                                        )
                                    ) ||
                                    // ExternalRef Reference
                                    (
                                        r.Name.LocalName == "Reference" &&
                                        externalRefAssemblies.Any(p => r.Attribute("Include").Value.StartsWith(p)) &&
                                        (
                                    // SpecificVersion deve sempre ser false
                                            r.Element(XName.Get("SpecificVersion", r.Name.NamespaceName)) == null ||
                                            Convert.ToBoolean(r.Element(XName.Get("SpecificVersion", r.Name.NamespaceName)).Value) == true ||
                                    // Copylocal deve sempre ser false
                                            r.Element(XName.Get("Private", r.Name.NamespaceName)) == null ||
                                            Convert.ToBoolean(r.Element(XName.Get("Private", r.Name.NamespaceName)).Value) == true
                                        )
                                    ) ||
                                    // ProjectReference
                                    (
                                        r.Name.LocalName == "ProjectReference" &&
                                        GacAssemblies.Any(p => r.Attribute("Include").Value.Contains(p)) &&
                                        (
                                    // Copylocal deve sempre ser false
                                            r.Element(XName.Get("Private", r.Name.NamespaceName)) == null ||
                                            Convert.ToBoolean(r.Element(XName.Get("Private", r.Name.NamespaceName)).Value) == true
                                        )
                                    )
                                select r;

            if (frameworkRefs.Count() > 0)
            {
                foreach (XElement item in frameworkRefs)
                {
                    // Framework Reference
                    if (item.Name.LocalName == "Reference" &&
                        BaseAssemblyNames.Any(g => item.Attribute("Include").Value.StartsWith(g)))
                    {
                        item.SetElementValue(XName.Get("SpecificVersion", item.Name.NamespaceName), Convert.ToString(item.Attribute("Include").Value.StartsWith(SpecificVersionAssemblies)));

                        if (item.Element(XName.Get("HintPath", item.Name.NamespaceName)) != null)
                            item.SetElementValue(XName.Get("HintPath", item.Name.NamespaceName), GetRelativePath(file, item.Element(XName.Get("HintPath", item.Name.NamespaceName)).Value));

                        if (!file.Contains("Deployment") && !file.Contains("Tools"))
                        {
                            if (GacAssemblies.Any(g => item.Attribute("Include").Value.StartsWith(g)))
                                item.SetElementValue(XName.Get("Private", item.Name.NamespaceName), Convert.ToString(false));
                            else
                                item.SetElementValue(XName.Get("Private", item.Name.NamespaceName), null);
                        }

                        fileUpdated = true;
                    }
                    // ExternalRef Reference
                    else if (item.Name.LocalName == "Reference" &&
                             externalRefAssemblies.Any(p => item.Attribute("Include").Value.StartsWith(p)))
                    {
                        item.SetElementValue(XName.Get("SpecificVersion", item.Name.NamespaceName), Convert.ToString(false));

                        if (!file.Contains("Deployment") && !file.Contains("Tools"))
                            item.SetElementValue(XName.Get("Private", item.Name.NamespaceName), Convert.ToString(false));

                        fileUpdated = true;
                    }
                    // ProjectReference
                    else if (item.Name.LocalName == "ProjectReference" &&
                             GacAssemblies.Any(g => item.Attribute("Include").Value.Contains(g)))
                    {
                        if (!file.Contains("Deployment") && !file.Contains("Tools"))
                            item.SetElementValue(XName.Get("Private", item.Name.NamespaceName), Convert.ToString(false));

                        fileUpdated = true;
                    }
                }
            }

            return fileUpdated;
        }

        private static bool ChangeProjectsFrameworkReferences(XDocument project, string file, List<FrameworkAssembly> frameworkAssemblies)
        {
            bool fileUpdated = false;

            // Captura todas as tags Reference em que o atributo Include aponte para uma dll de infra e 
            // que tenham algum dos elementos SpecificVersion, HintPath ou Private incorretos
            var frameworkRefs = (from r in project.Descendants()
                                 where
                                     r.Name.LocalName == "Reference"

                                 join a in frameworkAssemblies on new AssemblyName(r.Attribute("Include").Value).Name equals a.AssemblyName
                                 where
                                     // SpecificVersion deve ser true para PSE e false para todas as outras
                                     r.Element(XName.Get("SpecificVersion", r.Name.NamespaceName)) == null ||
                                     Convert.ToBoolean(r.Element(XName.Get("SpecificVersion", r.Name.NamespaceName)).Value) != a.SpecificVersion ||
                                     // HintPath deve estar correto
                                     r.Element(XName.Get("HintPath", r.Name.NamespaceName)) == null ||
                                     r.Element(XName.Get("HintPath", r.Name.NamespaceName)).Value != GetRelativePath(file, a.HintPath) ||
                                     // Copylocal deve sempre ser false
                                     r.Element(XName.Get("Private", r.Name.NamespaceName)) == null ||
                                     Convert.ToBoolean(r.Element(XName.Get("Private", r.Name.NamespaceName)).Value) == true

                                 select new Reference_FrameworkAssembly() { Reference = r, Assembly = a }
                                ).Distinct(new Reference_FrameworkAssemblyComparer());

            if (frameworkRefs.Count() > 0)
            {
                foreach (Reference_FrameworkAssembly item in frameworkRefs)
                {
                    item.Reference.SetElementValue(XName.Get("SpecificVersion", item.Reference.Name.NamespaceName), Convert.ToString(item.Assembly.SpecificVersion));
                    item.Reference.SetElementValue(XName.Get("HintPath", item.Reference.Name.NamespaceName), GetRelativePath(file, item.Assembly.HintPath));
                    item.Reference.SetElementValue(XName.Get("Private", item.Reference.Name.NamespaceName), Convert.ToString(false));
                }

                fileUpdated = true;
            }

            return fileUpdated;
        }

        private static bool ChangeBusinessProjectsReferences(XDocument project, string file, List<FrameworkAssembly> frameworkAssemblies)
        {
            bool fileUpdated = false;

            // Captura todas as tags Reference em que o atributo Include aponte para uma dll de negócio e 
            // que tenham algum dos elementos SpecificVersion, HintPath ou Private incorretos
            var businessRefs = from r in project.Descendants()
                               where
                                   r.Name.LocalName == "Reference" &&

                                   // Não pode ser uma Assembly de infra e deve iniciar com o nome do sistema
                                   !frameworkAssemblies.Any(a => new AssemblyName(r.Attribute("Include").Value).Name == a.AssemblyName) &&
                                   BaseAssemblyNames.Any(b => r.Attribute("Include").Value.StartsWith(b)) &&
                                   (
                                   // SpecificVersion deve sempre ser false
                                       r.Element(XName.Get("SpecificVersion", r.Name.NamespaceName)) == null ||
                                       Convert.ToBoolean(r.Element(XName.Get("SpecificVersion", r.Name.NamespaceName)).Value) == true ||
                                   // HintPath não pode existir ou deve estar correto
                                       (
                                           r.Element(XName.Get("HintPath", r.Name.NamespaceName)) != null &&
                                           r.Element(XName.Get("HintPath", r.Name.NamespaceName)).Value != GetRelativePath(file, r.Element(XName.Get("HintPath", r.Name.NamespaceName)).Value)
                                       ) ||
                                   // Private não pode existir
                                       r.Element(XName.Get("Private", r.Name.NamespaceName)) != null
                                   )
                               select r;

            if (businessRefs.Count() > 0)
            {
                foreach (XElement item in businessRefs)
                {
                    item.SetElementValue(XName.Get("SpecificVersion", item.Name.NamespaceName), Convert.ToString(false));
                    item.SetElementValue(XName.Get("Private", item.Name.NamespaceName), null);

                    if (item.Element(XName.Get("HintPath", item.Name.NamespaceName)) == null)
                    {
                        continue;
                    }

                    if (!item.Element(XName.Get("HintPath", item.Name.NamespaceName)).Value.Contains("Compilados"))
                    {
                        Log(string.Format("The hint path of reference '{0}' doesn't contain 'Compilados'. HintPath: '{1}'", item.Attribute("Include").Value, item.Element(XName.Get("HintPath", item.Name.NamespaceName)).Value), file);
                        continue;
                    }

                    item.SetElementValue(XName.Get("HintPath", item.Name.NamespaceName), GetRelativePath(file, item.Element(XName.Get("HintPath", item.Name.NamespaceName)).Value));
                }

                fileUpdated = true;
            }

            return fileUpdated;
        }

        private static bool ChangeOutputPath(XDocument project, string file)
        {
            bool fileUpdated = false;

            string basePath = GetBasePath(file);
            string debugOutputPath = null;
            string releaseOutputPath = null;

            // Captura tags OutputPath
            var outputPaths = from o in project.Descendants()
                              where
                                  o.Name.LocalName == "OutputPath"
                              select o;

            if (outputPaths.Count() == 2)
            {
                if (!string.IsNullOrEmpty(basePath))
                {
                    debugOutputPath = outputPaths.ToList()[0].Value;
                    releaseOutputPath = outputPaths.ToList()[1].Value;

                    if (!debugOutputPath.Contains("Compilados") && !releaseOutputPath.Contains("Compilados"))
                    {
                        Log(string.Format("OutputPath doesn't contain 'Compilados'. Debug: '{0}', Release: '{1}'", debugOutputPath, releaseOutputPath), file);
                    }
                    else
                    {
                        // Corrige o debug path
                        if (debugOutputPath.Contains("Compilados") &&
                            GetRelativePath(file, debugOutputPath, basePath) != debugOutputPath)
                        {
                            debugOutputPath = GetRelativePath(file, debugOutputPath, basePath);

                            fileUpdated = true;
                        }

                        // Se o release path estiver diferente, deixa os dois iguais
                        if (debugOutputPath != releaseOutputPath)
                        {
                            releaseOutputPath = debugOutputPath;

                            fileUpdated = true;
                        }

                        outputPaths.ToList()[0].Value = debugOutputPath;
                        outputPaths.ToList()[1].Value = releaseOutputPath;
                    }
                }
            }
            else
            {
                Log("OutputPath not found", file);
            }


            // Captura tags DocumentationFile
            var documentationFile = from o in project.Descendants()
                                    where
                                        o.Name.LocalName == "DocumentationFile" &&
                                        !string.IsNullOrEmpty(o.Value)
                                    select o;

            if (documentationFile.Count() > 0)
            {
                if (!string.IsNullOrEmpty(basePath))
                {
                    string debug = documentationFile.ToList()[0].Value;
                    string release = string.Empty;
                    if (documentationFile.Count() > 1)
                        release = documentationFile.ToList()[1].Value;

                    if (string.IsNullOrEmpty(debugOutputPath))
                    {
                        // Se não achou OutputPath tenta corrigir DocumentationFile manualmente
                        if (!debug.Contains("Compilados"))
                        {
                            Log(string.Format("DocumentationFile doesn't contain 'Compilados'. Debug: '{0}', Release: '{1}'", debug, release), file);
                        }
                        else
                        {
                            if (GetRelativePath(file, debug, basePath) != debug)
                            {
                                debug = GetRelativePath(file, debug, basePath);
                                release = debug;

                                fileUpdated = true;
                            }
                        }
                    }
                    else if (Path.GetDirectoryName(debug) != debugOutputPath)
                    {
                        // Se achou OutputPath, deixa o DocumentationFile igual ao OutputPath
                        debug = debugOutputPath + (!debugOutputPath.EndsWith("\\") ? "\\" : null) + Path.GetFileName(debug);
                        release = debug;

                        fileUpdated = true;
                    }

                    documentationFile.ToList()[0].Value = debug;

                    if (documentationFile.Count() > 1)
                    {
                        documentationFile.ToList()[1].Value = release;
                    }
                    else if (outputPaths.Count() == 2)
                    {
                        // Cria tag DocumentationFile de release se não existir
                        outputPaths.ToList()[1].Parent.SetElementValue(XName.Get("DocumentationFile", outputPaths.ToList()[1].Parent.Name.NamespaceName), release);

                        fileUpdated = true;
                    }
                }
            }

            return fileUpdated;
        }

        private static bool RemoveServiceReferences(XDocument project, string file)
        {
            bool fileUpdated = false;

            // Captura tags com referencias para arquivos indevidos
            var tags = from o in project.Descendants()
                       where
                           o.Attribute("Include") != null &&
                           (
                               o.Attribute("Include").Value.EndsWith(".xsd") ||
                               o.Attribute("Include").Value.EndsWith(".datasource") ||
                               o.Attribute("Include").Value.EndsWith(".wsdl") ||
                               o.Attribute("Include").Value.EndsWith(".svcinfo")
                           )
                       select o;

            if (tags.Count() > 0)
            {
                foreach (XElement tag in tags.ToList())
                {
                    if (tag.Parent.Descendants().Count() == 1)
                        tag.Parent.Remove();
                    else
                        tag.Remove();
                }

                fileUpdated = true;
            }

            return fileUpdated;
        }

        private static List<FrameworkAssembly> GetFrameworkAssemblies()
        {
            // Captura todas a dlls de infra

            List<string> files = new List<string>();
            foreach (string folder in FrameworkAssembliesFolders.Reverse<string>())
            {
                string path = Path.GetFullPath(RootPath + folder);
                if (Directory.Exists(path))
                    files = Directory.GetFiles(path, "*.dll", SearchOption.AllDirectories).Concat(files).ToList();
            }

            List<FrameworkAssembly> assemblies = new List<FrameworkAssembly>();

            foreach (string file in files)
            {
                FrameworkAssembly assembly = new FrameworkAssembly()
                {
                    AssemblyName = Path.GetFileNameWithoutExtension(file),
                    FullPath = file,
                    SpecificVersion = Path.GetFileNameWithoutExtension(file).StartsWith(SpecificVersionAssemblies),
                    HintPath = file.Replace(Path.GetPathRoot(file), "\\")
                };

                if (assemblies.Any(a => a.AssemblyName == assembly.AssemblyName))
                    throw new Exception(string.Format("The assembly '{0}' alerady exists on path '{1}'", assembly.FullPath, assemblies.First(a => a.AssemblyName == assembly.AssemblyName).FullPath));

                assemblies.Add(assembly);
            }

            return assemblies;
        }

        private static List<string> GetExternalRefAssemblies(string teamProject, string path)
        {
            // Captura todas as dlls de ExternalRef

            string externalRefPath = path + ExternalRefSourceFolders[teamProject];

            List<string> references = new List<string>();

            foreach (string file in Directory.GetFiles(externalRefPath, "*.dll", SearchOption.AllDirectories))
                references.Add(Path.GetFileNameWithoutExtension(file));

            return references.Distinct().ToList();
        }

        private static string GetRelativePath(string projectPath, string originalPath)
        {
            string basePath = GetBasePath(originalPath);

            return GetRelativePath(projectPath, originalPath, basePath);
        }

        private static string GetRelativePath(string projectPath, string originalPath, string basePath)
        {
            if (!string.IsNullOrEmpty(basePath))
            {
                // Calcula quantas pastas deve descer até o workspace
                int levels = Path.GetDirectoryName(projectPath).Replace(RootPath, "").Split('\\').Length - 1;


                string relativePath = Regex.Replace(originalPath, string.Format(@".*({0}|\\Compilados)(.*)", basePath.Replace(@"\", @"\\")), basePath + @"$2");

                for (int i = 0; i < levels; i++)
                {
                    relativePath = (i == 0 ? ".." : "..\\") + relativePath;
                }

                return relativePath;
            }
            else
            {
                return originalPath;
            }
        }

        private static void Log(string message, string file)
        {
            File.AppendAllText(Path.GetFullPath(LogFile), string.Format("{0} {1}", file, message));
            File.AppendAllText(Path.GetFullPath(LogFile), System.Environment.NewLine);
        }

        private static string GetBasePath(string path)
        {
            if (!BaseAssemblyPaths.Keys.Any(t => path.Contains(t)))
            {
                return null;
            }

            return BaseAssemblyPaths[BaseAssemblyPaths.Keys.First(t => path.Contains(t))];
        }
    }

}
